﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace PerfAnalyzerLib
{
	/// <summary>
	/// Manage the argument given to an executable.
	/// </summary>
	public class ArgumentManager
	{
		private List<IArgument> _arguments = new List<IArgument>();

		public string Description = string.Empty;

		public ReadOnlyCollection<IArgument> Arguments { get { return _arguments.AsReadOnly(); } }

		/// <summary>
		/// Add argument definition to the argument manager.
		/// </summary>
		/// <param name="name">Name of parameter (with the -).</param>
		/// <param name="isMandatory">Is parameter mandatory.</param>
		/// <param name="isKeyValue">Is a key/value type of parameter or simple flag.</param>
		public IArgument AddValueKeyArgument<T>(string name, bool isMandatory, string description)
		{
			if (IsParameterNameAlreadyUsed(name))
			{
				throw new ArgumentException();
			}

			var newArg = new Argument<T>(name, description, isMandatory);
			_arguments.Add(newArg);

			return newArg;
		}

		/// <summary>
		/// Add argument definition as flag.
		/// </summary>
		/// <param name="name">Name of parameter.</param>
		/// <param name="description">Description of parameter.</param>
		public void AddFlagArgument(string name, string description)
		{
			if (IsParameterNameAlreadyUsed(name))
			{
				throw new ArgumentException();
			}

			_arguments.Add(new Argument<bool>(name, description, isFlag: true));
		}

		/// <summary>
		/// Parse the given arguments.
		/// </summary>
		/// <param name="args">Arguments.</param>
		public bool Parse(string[] args, bool exitUponFailure)
		{
			// Go through all arguments
			for (int i = 0; i < args.Length; i++)
			{
				string arg = args[i];

				var  argument = _arguments.Find(a => a.Name == arg);
				if (null == argument)
				{
					Console.WriteLine("Unknown argument: " + arg);
					PrintHelp();

					if (exitUponFailure)
						Environment.Exit(-1);
					return false;
				}

				//argument.AddValue(args)


				// if only a flag
				if (argument.IsFlag)
				{
					argument.AddValue("true");
				}
				else
				{
					// Read the next argument and convert it
					if (args.Length > i)
					{
						argument.AddValue(args[++i]);
						continue;
					}
					else
					{
						// Error
						Console.WriteLine("Missing value for the argument " + arg + ".");
						PrintHelp();
						Environment.Exit(-1);
					}
				}
			}

			// Verify if mandatory arguments are there
			foreach (var arg in _arguments)
			{
				if (arg.IsMandatory && !arg.IsPresent)
				{
					Console.WriteLine("Missing mandatory argument: " + arg.Name + ".");
					PrintHelp();

					if (exitUponFailure)
						Environment.Exit(-1);

					return false;
				}
			}

			return true;
		}

		/// <summary>
		/// Print the help on the console.
		/// </summary>
		public void PrintHelp()
		{
			Console.WriteLine();
			Console.WriteLine("Description:");
			Console.WriteLine(Description);
			Console.WriteLine();

			foreach (var arg in _arguments)
			{
				string line = "\t";
				line += arg.Name + " \t";
				if (arg.IsMandatory)
				{
					line += "[mandatory] ";
				}
				line += arg.Description;

				Console.WriteLine(line);
			}
		}

		private bool IsParameterNameAlreadyUsed(string argumentName)
		{
			return _arguments.Any(arg => arg.Name == argumentName);
		}
	}
}
